use riscv::register::sstatus::{ self, Sstatus, SPP };

#[repr(C)] // 使用 C 风格的内存布局
pub struct  TrapContext {
    pub x: [usize; 32],
    pub sstatus: Sstatus,
    pub sepc: usize,
    // 在应用初始化的时候由内核写入应用地址空间中的 TrapContext 的相应位置，此后就不再被修改
    pub kernel_satp: usize, // 表示内核地址空间的 token
    pub kernel_sp: usize,   // 表示当前应用在内核地址空间中的内核栈栈顶的虚拟地址
    pub trap_handler: usize,// 表示内核中 trap handler 入口点的虚拟地址
}
// x0 被硬编码为 0 ，它自然不会有变化；
// 还有 tp(x4) 寄存器，除非我们手动出于一些特殊用途使用它，否则一般也不会被用到。
// 虽然它们无需保存， 但我们仍然在 TrapContext 中为它们预留空间，主要是为了后续的实现方便

impl TrapContext {
    pub fn set_sp(&mut self, sp: usize) { 
        // reg x2 -> sp
        self.x[2] = sp; 
    }
    pub fn app_init_context(
        entry: usize, 
        sp: usize,
        kernel_satp: usize,
        kernel_sp: usize,
        trap_handler: usize
    ) -> Self {
        let mut sstatus = sstatus::read();
        sstatus.set_spp(SPP::User); // 设置特权级
        let mut ctx = Self {
            x: [0; 32],
            sstatus,
            sepc: entry, // 指向 ecall
            kernel_satp,
            kernel_sp,
            trap_handler,
        };
        ctx.set_sp(sp); // 设置 sp
        ctx
    }
}
